package org.elanfox.cloud.redis.config;

import io.lettuce.core.RedisException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.elanfox.cloud.redis.RedisLocker;
import org.elanfox.cloud.redis.annotations.RedisLock;
import org.elanfox.cloud.redis.base.LockType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author allan
 * @date 2019-08-15
 */
@Component
@Aspect
public class RedisInterceptor {
    @Autowired
    private RedisLocker redisLocker;

    @Around("@annotation(redisLock)")
    public void around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        LockType lockType = redisLock.lockType();

        if (lockType == LockType.BLOCK) {
            while (true) {
                //尝试获取锁
                boolean locked = redisLocker.tryLock(redisLock.lockKey(), redisLock.serviceId(), redisLock.timeout());
                if (locked) {
                    //获取锁成功执行方法
                    proceed(joinPoint, redisLock);
                    break;
                }
            }
        } else {
            //尝试获取锁
            boolean locked = redisLocker.tryLock(redisLock.lockKey(), redisLock.serviceId(), redisLock.timeout());
            if (locked) {
                //获取锁成功执行方法
                proceed(joinPoint, redisLock);
            } else {
                throw new RedisException("获取锁失败");
            }
        }
    }

    private void proceed(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        //获取锁成功执行方法
        try {
            joinPoint.proceed();
            redisLocker.release(redisLock.lockKey(), redisLock.serviceId());
        } catch (Throwable throwable) {
            //释放锁
            redisLocker.release(redisLock.lockKey(), redisLock.serviceId());
            throw throwable;
        }
    }
}
